import { Body, Controller, Delete, Param, ParseIntPipe, Post, Put } from '@nestjs/common';
import { BaseController } from '../base/base.controller';
import dto, { AddDto, PageDto, RechargePageDto, UpdateDto } from './dto';
import { DtoPipe } from '../pipes/dto/dto.pipe';
import { DbService } from '@app/db';
import { CacheService } from '@app/cache';
import { ChargingCard } from '@app/db/entities/ChargingCard';
import { FindOptionsWhere, In } from 'typeorm';
import { CardRechargeOrder } from '@app/db/entities/CardRechargeOrder';

@Controller('chargingCard')
export class ChargingCardController extends BaseController {
    constructor(
        private db:DbService,
        private cache:CacheService
    ){
        super()
    }

    @Post('recharge/page')
    async rechargePage(@Body(new DtoPipe({skipMissingProperties : false})) dto:RechargePageDto) {

        let result:PaginationResult<CardRechargeOrder & {cardNo?:string}> = {
            total : 0,
            rows : []
        }

        let where:FindOptionsWhere<CardRechargeOrder> = {
            
        }

        if(dto.cardNo) {
            const card = await this.db.chargingCard.findOne({cardNo : dto.cardNo}, ['id'])
            if(!card) {
                return this.successRes(result)
            }

            where.cardId = card.id
        }
        
        if(dto.orderNo) {
            where.orderNo = dto.orderNo
        }

        result.total = await this.db.cardRechargeOrder.count(where)

        if(result.total > 0) {
            result.rows = await this.db.cardRechargeOrder.pagination(dto.pageNo, dto.pageSize, where, null, {id : 'DESC'})
            const cardIds:number[] = []
            result.rows.forEach(row => {
                if(!cardIds.includes(row.cardId)) {
                    cardIds.push(row.cardId)
                }
            })

            const cards = await this.db.chargingCard.find({id : cardIds.length == 1 ? cardIds[0] : In(cardIds)}, ['id', 'cardNo'])

            result.rows.forEach(row => {
                const card = cards.find(v => v.id == row.cardId)
                if(card) {
                    row.cardNo = card.cardNo
                }
            })

        }
        

        return this.successRes(result)

    }

    @Post('page')
    async page(@Body(new DtoPipe({skipMissingProperties : false})) dto:PageDto) {

        let result:PaginationResult<ChargingCard> = {
            total : 0,
            rows : []
        }

        let where:FindOptionsWhere<ChargingCard> = {
            
        }
        
        if(dto.cardNo) {
            where.cardNo = dto.cardNo
        }

        result.total = await this.db.chargingCard.count(where)

        if(result.total > 0) {
            result.rows = await this.db.chargingCard.pagination(dto.pageNo, dto.pageSize, where, null, {id : 'DESC'})

        }
        

        return this.successRes(result)

    }

    @Post('add')
    async add(@Body(new DtoPipe({skipMissingProperties : false})) dto:AddDto) {
        const lockKey = 'add:card'
        const lock = await this.cache.set(lockKey, '1', 10, 'NX')
        if(!lock) return this.failRes()

        const cardNos = [...new Set(dto.cardNos)];
        if(cardNos.length == 0) return

        const cards = await this.db.chargingCard.find({cardNo : cardNos.length == 1 ? cardNos[0] : In(cardNos)}, ['cardNo'])

        if(cards.length > 0) {
            this.cache.del(lockKey)
            if(cardNos.length == 1) {
                return this.failRes('卡号已经存在')
            }
            return this.failRes('卡号已经存在', {code : -1, cardNos : cards.map(v => v.cardNo)})
        }
        const items = cardNos.map(card => ({cardNo : card, remark : dto.remark, status : 0, createTime : this.utils.time()}))

        const r = await this.db.chargingCard.insert(items)
        this.cache.del(lockKey)
        return r.affectedRows > 0 ? this.successRes() : this.failRes()
    }

    @Put('update/:id')
    async update(@Body(new DtoPipe({skipMissingProperties : true})) dto:UpdateDto, @Param('id', ParseIntPipe) id:number) {
        if(isNaN(id) || id < 1) return
        let fields = Object.keys(dto) as (keyof ChargingCard)[]
        if(fields.length == 0) return

        const card = await this.db.chargingCard.findOne({id : id})
        if(!card) return

        let updateData:KeyToValueType<ChargingCard> = {}
        for(let k in card) {
            if(k in dto && card[k] != dto[k]) {
                updateData[k] = dto[k]
            }
        }

        if(Object.keys(updateData).length == 0) return this.successRes()

        let lockKey;
        if(updateData.cardNo != undefined) {
            lockKey = 'add:card:' + updateData.cardNo
            const lock = await this.cache.set(lockKey, '1', 10, 'NX')
            if(!lock) return this.failRes('卡号已经存在')

            if((await this.db.chargingCard.isExistsByWhere({cardNo : updateData.cardNo})) == true) {
                this.cache.del(lockKey)
                return this.failRes('卡号已经存在')
            }
        }

        const r = await this.db.chargingCard.update({id : id}, updateData)
        if(lockKey != undefined) {
            this.cache.del(lockKey)
        }
        return r > 0 ? this.successRes() : this.failRes()
    }

    @Delete('delete/:id')
    async delete(@Param('id', ParseIntPipe) id:number) {
        if(isNaN(id) || id < 1) return
        const res = await this.db.chargingCard.delete({id : id})
        return res === 0 ? this.failRes() : this.successRes()
    }
}
